package labox.innovation.gameserver.skills;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import labox.innovation.config.Config;
import labox.innovation.gameserver.model.L2ItemInstance;
import labox.innovation.gameserver.model.L2Skill;
import labox.innovation.gameserver.model.actor.L2Character;
import labox.innovation.gameserver.model.actor.L2Npc;
import labox.innovation.gameserver.model.actor.L2Playable;
import labox.innovation.gameserver.model.actor.L2Summon;
import labox.innovation.gameserver.model.actor.instance.FuzePcInstance;
import labox.innovation.gameserver.model.actor.instance.L2PetInstance;
import labox.innovation.gameserver.model.itemcontainer.Inventory;
import labox.innovation.gameserver.network.SystemMessageId;
import labox.innovation.gameserver.network.serverpackets.SystemMessage;
import labox.innovation.gameserver.skills.funcs.Func;
import labox.innovation.gameserver.templates.chars.PlayerTemplate;
import labox.innovation.gameserver.templates.effects.EffectTemplate;
import labox.innovation.gameserver.templates.item.L2Item;
import labox.innovation.gameserver.templates.item.L2Weapon;
import labox.innovation.gameserver.templates.item.L2WeaponType;
import labox.innovation.gameserver.templates.skills.L2EffectType;
import labox.innovation.gameserver.templates.skills.L2SkillType;
import labox.innovation.gameserver.util.Util;
import labox.innovation.util.Rnd;

/**
 * ȫ�ּ������빫ʽ�����࣬���и������ԵĶ�������
 */
public final class Formulas {

	/** Regen Task period */
	protected static final Logger _log = LoggerFactory.getLogger(Formulas.class.getName());

	private static final int HP_REGENERATE_PERIOD = 6000; // 6 secs

	public static final byte SKILL_REFLECT_FAILED = 0; // no reflect

	public static final byte SKILL_REFLECT_SUCCEED = 1; // normal reflect, some damage reflected some other not

	public static final byte SKILL_REFLECT_VENGEANCE = 2; // 100% of the damage affect both

	private static final byte MELEE_ATTACK_RANGE = 40;

	public static final int MAX_STAT_VALUE = 100;

	/**
	 * HP/MP�Իָ��ĵ���ʱ�䣨ms�� .<BR>
	 * <BR>
	 */
	public static int getRegeneratePeriod(L2Character cha) {
		return HP_REGENERATE_PERIOD;
	}

	/**
	 * Return the standard NPC Calculator set containing ACCURACY_COMBAT and EVASION_RATE.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * A calculator is created to manage and dynamically calculate the effect of a character property (ex : MAX_HP, REGENERATE_HP_RATE...). In fact, each calculator is a table of Func object in which each Func represents a mathematic function : <BR>
	 * <BR>
	 * 
	 * FuncAtkAccuracy -> Math.sqrt(_player.getDEX())*6+_player.getLevel()<BR>
	 * <BR>
	 * 
	 * To reduce cache memory use, L2NPCInstances who don't have skills share the same Calculator set called <B>NPC_STD_CALCULATOR</B>.<BR>
	 * <BR>
	 * 
	 */
	public static Calculator[] getStdNPCCalculators() {
		Calculator[] std = new Calculator[Stats.NUM_STATS];

		return std;

	}

	public static Calculator[] getStdDoorCalculators() {
		//		Calculator[] std = new Calculator[Stats.NUM_STATS];
		//
		//		return std;
		//TODO:
		//��ɾ��
		return null;
	}

	/**
	 * Add basics Func objects to L2PcInstance and L2Summon.<BR>
	 * <BR>
	 * 
	 * <B><U> Concept</U> :</B><BR>
	 * <BR>
	 * A calculator is created to manage and dynamically calculate the effect of a character property (ex : MAX_HP, REGENERATE_HP_RATE...). In fact, each calculator is a table of Func object in which each Func represents a mathematic function : <BR>
	 * <BR>
	 * 
	 * FuncAtkAccuracy -> Math.sqrt(_player.getDEX())*6+_player.getLevel()<BR>
	 * <BR>
	 * 
	 * @param cha
	 *            L2PcInstance or L2Summon that must obtain basic Func objects
	 */
	public static void addFuncsToNewCharacter(L2Character cha) {
	}

	/**
	 * Calculated damage caused by ATTACK of attacker on target, called separatly for each weapon, if dual-weapon is used.
	 * 
	 * @param attacker
	 *            player or NPC that makes ATTACK
	 * @param target
	 *            player or NPC, target of ATTACK
	 * @param miss
	 *            one of ATTACK_XXX constants
	 * @param crit
	 *            if the ATTACK have critical success
	 * @param dual
	 *            if dual weapon is used
	 * @param ss
	 *            if weapon item was charged by soulshot
	 * @return damage points �������� �˺�
	 */
	public static final double calcPhysDam(L2Character attacker, L2Character target, L2Skill skill, boolean crit) {
		double damage;

		if (skill != null) {
			damage = skill.getPower(attacker);
		} else {
			damage = attacker.getPAtk();
		}

		damage -= target.getPDef();
		if (crit) {
			damage *= Config.CRIT_DMG_RATE;
		}

		return damage;
	}

	/**
	 * ���������˺� ����Ŀ�����
	 * 
	 * @param attacker
	 * @param skill
	 * @param crit
	 * @return
	 */
	public static final double calcPhysDam(L2Character attacker, L2Skill skill, boolean crit) {
		double damage;

		if (skill != null) {
			damage = skill.getPower(attacker);
		}else{
			damage = attacker.getPAtk();
		}
		
		if (crit) {
			damage *= Config.CRIT_DMG_RATE;
		}

		return damage;
	}

	/**
	 * ���㹥��״̬�����У�������Ϊ���е�
	 * 
	 * @return 0:δ���У� 1:���У� 2:����
	 */
	public static final int calcAttactHit(L2Character attacker, L2Character target) {
		/*
		 * �����ߵȼ�>=��������
		        δ����=max(0,1000-(��������-��������+min(100,20*(max(3,���ȼ�-���ȼ�)-3))))
		�����ȼ�<��������
		    δ����= max (0,1000-(��������-��������-min(100,20*(max(3,���ȼ�-���ȼ�)-3))))
		    ����=������������
		   ����=min(1000,max(0,1000-δ����-����))
		��ע��
		����㷨��ͨ��һ�������ɵģ������ֵ[0��1000)��
		 ����������£����ǹ������������Ĺ��������ܣ�*1.5��Ȼ�������������㡣

		 * 
		 */

		// ����miss��
		int miss = Formulas.calcHitMiss(attacker, target);

		//��ñ�����
		int crit = attacker.getCriticalHit();

		int rnd = Rnd.get(0, 1000);

		if (rnd < miss) {
			return 0; //δ����
		} else if (rnd < miss + crit) {
			return 2; //����
		}

		return 1; //����

	}

	/**
	 * ����ħ���˺�
	 * 
	 * @param attacker
	 * @param target
	 * @param skill
	 * @param mcrit
	 * @return
	 */
	public static final double calcMagicDam(L2Character attacker, L2Character target, L2Skill skill, boolean mcrit) {

		double mDef = target.getMDef();

		double damage = skill.getPower(attacker);
		if (_log.isDebugEnabled()) {
			_log.debug("����ħ���˺�: " + damage + "Ŀ���ħ������:" + mDef);
		}

		damage -= mDef;

		if (mcrit) {

			damage *= Config.CRIT_DMG_RATE;
			if (_log.isDebugEnabled()) {

				_log.debug("����ħ���˺�����ֵ: " + damage);
			}

		}

		return damage;

	}

	/**
	 * ���� ħ���Ƿ񱩻�
	 */
	public static final boolean calcMCrit(double mRate) {
		return mRate > Rnd.get(1000);
	}

	/**
	 * ���� ������ȴʱ��
	 * 
	 * @param attacker
	 * @param skill
	 * @param skillTime
	 * @return
	 */
	public static final int calcAtkSpd(L2Character attacker, L2Skill skill, double skillTime) {
		return (int) skillTime;
	}

	/**
	 * ���� ����miss��
	 **/
	public static int calcHitMiss(L2Character attacker, L2Character target) {
		/*
		 �����ߵȼ�>=��������
		δ����=max(0,1000-(��������-��������+min(100,20*(max(3,���ȼ�-���ȼ�)-3))))
		�����ȼ�<��������
		δ����= max (0,1000-(��������-��������-min(100,20*(max(3,���ȼ�-���ȼ�)-3))))
		 */

		int difflevel = attacker.getLevel() - target.getLevel();
		int value;
		if (difflevel >= 0) {
			value = Math.max(0, 1000 - (attacker.getAccuracy() - target.getEvasionRate() + Math.min(100, 20 * (Math.max(3, difflevel) - 3))));
		} else {
			value = Math.max(0, 1000 - (attacker.getAccuracy() - target.getEvasionRate() - Math.min(100, 20 * (Math.max(3, -difflevel) - 3))));
		}

		return value;
	}

	/**
	 * ���� Ч��ĳɹ���
	 * 
	 * @param multiplier
	 * @param attacker
	 * @param target
	 * @param type
	 * @return
	 */
	public static int calcEffectTypeProficiency(int multiplier, L2Character attacker, L2Character target, L2EffectType type) {

		if (type != null && target != null && attacker != null) {
			switch (type) {
			case BLEED:
				multiplier -= target.getBleedVuln();
				multiplier += attacker.getBleedVuln();
				break;
			case POISON:
				multiplier -= target.getPoisonVuln();
				multiplier += attacker.getPoisonVuln();
				break;
			case STUN:
				multiplier -= target.getStunVuln();
				multiplier += attacker.getStunVuln();
				break;
			case ROOT:
				multiplier -= target.getRootVuln();
				multiplier += attacker.getRootVuln();
				break;
			case SLEEP:
				multiplier -= target.getSleepVuln();
				multiplier += attacker.getSleepVuln();
				break;
			case CONFUSION:
				multiplier -= target.getConfusionVuln();
				multiplier += attacker.getConfusionVuln();
				break;
			default:

			}

		}

		return multiplier;

	}

	/**
	 * ���㼼������ �ɹ����� ���� �� ��ʼֵ + �ͷ���ǿ������ - Ŀ��Ŀ���
	 * 
	 * @param multiplier
	 * @param attacker
	 * @param target
	 * @param type
	 * @return
	 */
	public static int calcSkillTypeProficiency(int multiplier, L2Character attacker, L2Character target, L2SkillType type) {

		if (type != null && target != null && attacker != null) {
			switch (type) {
			case BLEED:
				multiplier -= target.getBleedVuln();
				multiplier += attacker.getBleedVuln();
				break;
			case POISON:
				multiplier -= target.getPoisonVuln();
				multiplier += attacker.getPoisonVuln();
				break;
			case STUN:
				multiplier -= target.getStunVuln();
				multiplier += attacker.getStunVuln();
				break;
			case ROOT:
				multiplier -= target.getRootVuln();
				multiplier += attacker.getRootVuln();
				break;
			case SLEEP:
				multiplier -= target.getSleepVuln();
				multiplier += attacker.getSleepVuln();
				break;
			case CONFUSION:
				multiplier -= target.getConfusionVuln();
				multiplier += attacker.getConfusionVuln();
				break;
			default:

			}

		}

		return multiplier;
	}

	/**
	 * ���� Ч���Ƿ�ɹ� һ��ֻ��debuff�� ����Ҫ���� �ɹ����ʡ� ����Ѫ��ѣ�ε�
	 * 
	 * @param attacker
	 * @param target
	 * @param effect
	 * @param skill
	 * @return
	 */
	public static boolean calcEffectSuccess(L2Character attacker, L2Character target, EffectTemplate effect, L2Skill skill) {

		//		L2SkillType type = effect.effectType != null ? effect.effectType : skill.getSkillType();
		L2EffectType type = effect.effectType != null ? effect.effectType : null;

		//��ü���Ч��� �ɹ�����
		int value = (int) effect.effectPower;
		//���ռ���
		value = calcEffectTypeProficiency(value, attacker, target, type);

		if (Config.DEVELOPER)
			_log.info("������Ϊ:" + skill.getName() + "����Ч��ɹ�����Ϊ:" + value);
		return (Rnd.get(100) < value);

	}

	/**
	 * ���㼼�� �Ƿ�ɹ� һ��ֻ��debuff�� ����Ҫ���� �ɹ����ʡ� ����Ѫ��ѣ�ε�
	 * 
	 * @param attacker
	 * @param target
	 * @param skill
	 * @return
	 */
	public static boolean calcSkillSuccess(L2Character attacker, L2Character target, L2Skill skill) {
		//		if (skill.ignoreResists())
		//			return (Rnd.get(100) < skill.getPower());

		L2SkillType type = skill.getSkillType();

		//�������Ѫ�ȼ��� ��Ҫ���� �Ƿ�ֿ������⡣  ò�� ���ڲ���Ҫ�� ���� ��һֱ����True
		//�����Ҫ ���� ��ô��Ҫһ���ֶ� ��ʾ  ��Щ���ܵ� ���ܳɹ���
		int value = -1;
		if (value == -1) {
			return true;
		}
		value = calcSkillTypeProficiency(value, attacker, target, type);

		if (Config.DEVELOPER)
			_log.info(skill.getName() + ": ���ܳɹ�����:" + value);
		return (Rnd.get(100) < value);
	}

	/**
	 * ����ħ���Ƿ�ɹ�
	 * 
	 * @param attacker
	 * @param target
	 * @param skill
	 * @return
	 */
	public static boolean calcMagicSuccess(L2Character attacker, L2Character target, L2Skill skill) {
		double lvlDifference = (target.getLevel() - (skill.getMagicLevel() > 0 ? skill.getMagicLevel() : attacker.getLevel()));
		int rate = Math.round((float) (Math.pow(1.3, lvlDifference) * 100));

		return (Rnd.get(10000) > rate);
	}

	/**
	 * ���� mana�˺� ħ��ֵ�˺�
	 * 
	 * @param attacker
	 * @param target
	 * @param skill
	 * @param ss
	 * @param bss
	 * @return
	 */
	public static double calcManaDam(L2Character attacker, L2Character target, L2Skill skill, boolean ss, boolean bss) {
		//Mana Burnt = (SQR(M.Atk)*Power*(Target Max MP/97))/M.Def
		double mAtk = attacker.getMAtk();
		double mDef = target.getMDef();
		double mp = target.getMaxMp();
		if (bss)
			mAtk *= 4;
		else if (ss)
			mAtk *= 2;

		double damage = (Math.sqrt(mAtk) * skill.getPower(attacker) * (mp / 97)) / mDef;
		return damage;
	}

	/**
	 * ���� ���?�ܶ��
	 * 
	 * @param target
	 * @param skill
	 * @return
	 */
	public static boolean calcPhysicalSkillEvasion(L2Character target, L2Skill skill) {
		return false;//Rnd.get(100) < target.calcStat(Stats.P_SKILL_EVASION, 0, null, skill);
	}

	/**
	 * Calculate skill reflection according these three possibilities: <li>Reflect failed</li> <li>Mormal reflect (just effects). <U>Only possible for skilltypes: BUFF, REFLECT, HEAL_PERCENT, MANAHEAL_PERCENT, HOT, CPHOT, MPHOT</U></li> <li>vengEance reflect (100% damage reflected but damage is also dealt to actor). <U>This is only possible for skills with skilltype PDAM, BLOW, CHARGEDAM, MDAM or DEATHLINK</U></li> <br>
	 * <br>
	 * 
	 * @param actor
	 * @param target
	 * @param skill
	 * @return SKILL_REFLECTED_FAILED, SKILL_REFLECT_SUCCEED or SKILL_REFLECT_VENGEANCE ���� ���ܷ�������
	 */
	public static byte calcSkillReflect(L2Character target, L2Skill skill) {
		/*
		 *  Neither some special skills (like hero debuffs...) or those skills
		 *  ignoring resistances can be reflected
		 */
		//		if (skill.ignoreResists() || !skill.canBeReflected())
		//			return SKILL_REFLECT_FAILED;

		// only magic and melee skills can be reflected
		if (!skill.isMagic() && (skill.getCastRange() == -1 || skill.getCastRange() > MELEE_ATTACK_RANGE))
			return SKILL_REFLECT_FAILED;

		byte reflect = SKILL_REFLECT_FAILED;
		// check for non-reflected skilltypes, need additional retail check
		switch (skill.getSkillType()) {
		case BUFF:
		case REFLECT:
		case HEAL_PERCENT:
		case MANAHEAL_PERCENT:
		case HOT:
		case MPHOT:
		case AGGDEBUFF:
			return SKILL_REFLECT_FAILED;
			// these skill types can deal damage
		case PDAM:
		case MDAM:
			//			final Stats stat = skill.isMagic() ? Stats.VENGEANCE_SKILL_MAGIC_DAMAGE : Stats.VENGEANCE_SKILL_PHYSICAL_DAMAGE;
			//			final double venganceChance = target.getStat().calcStat(stat, 0, target, skill);
			//			if (venganceChance > Rnd.get(100))
			//				reflect |= SKILL_REFLECT_VENGEANCE;
			break;
		}

		//		final double reflectChance = target.calcStat(skill.isMagic() ? Stats.REFLECT_SKILL_MAGIC : Stats.REFLECT_SKILL_PHYSIC, 0, null, skill);
		//
		//		if (Rnd.get(100) < reflectChance)
		//			reflect |= SKILL_REFLECT_SUCCEED;

		return reflect;
	}

	/**
	 * ��������,����,����,����,���ʵ�������ֵ
	 * 
	 * @param target
	 * @param str
	 * @return
	 */
	public static final int calcBaseAble(L2Character target, int val) {
		return target.getLevel() + val;
	}

	/**
	 * �������?����������ֵ
	 * 
	 * @param target
	 * @param str
	 * @return
	 */
	public static final int calcPAtk(L2Character target, int val) {
		return target.getSTR() * 3 + val;
	}

	/**
	 * ����ħ��������������ֵ
	 * 
	 * @param target
	 * @param str
	 * @return
	 */
	public static final int calcMAtk(L2Character target, int val) {
		return target.getINT() * 3 + val;
	}

	/**
	 * ������������������ֵ �޸� zjj 2010.7.8 �ݲ߻�Ҫ���޸����������㹫ʽ
	 * 
	 * @param target
	 * @param str
	 * @return
	 */
	public static final int calcPDef(L2Character target, int val) {
		//		System.out.println(target.getName() + target.getTemplate()._id + "Ŀ������:" + target.getDEX() + "Ŀ�꾫��:" + target.getMEN() + "val:" + val );
		return (int) (target.getDEX() + target.getMEN() * 0.2 + val);
	}

	/**
	 * ����ħ�������������ֵ �޸� zjj 2010.7.8 �ݲ߻�Ҫ���޸�ħ��������㹫ʽ
	 * 
	 * @param target
	 * @param str
	 * @return
	 */
	public static final int calcMDef(L2Character target, int val) {
		return (int) (target.getDEX() * 0.2 + target.getMEN() + val);
	}

	/**
	 * ��ý�ɫ����HP��������ֵ
	 * 
	 * @return
	 */
	public static final int calcMaxHp(L2Character target, int val) {
		return target.getCON() * 15 + target.getLevel() * 5 + val;
	}

	/**
	 * ��ý�ɫ����MP��������ֵ
	 * 
	 * @return
	 */
	public static final int calcMaxMp(L2Character target, int val) {
		return target.getLevel() * 10 + val;
	}
}
